use std::error::Error;
use std::{env, fs};

pub struct Config {
  pub query: String,
  pub file_path: String,
  pub ignore_case: bool,
}

impl Config {
  // // args: &[String] 的旧写法
  // // Result 可能包含一个 Config 实例，也可能包含一条错误信息 &str. 且拥有 'static 生命周期
  // pub fn build(args: &[String]) -> Result<Config, &'static str> {
  //   if args.len() < 3 {
  //     return Err("not enough arguments");
  //   }
  //   // args.collect()
  //   let query = args[1].clone();
  //   let file_path = args[2].clone();

  //   // 检查环境配置里的 IGNORE_CASE 选项, 默认为 false. 传入 1 为 true.
  //   // 要想有, 命令行应该这么输入:
  //   // IGNORE_CASE=1 cargo run -- to poem.txt
  //   let ignore_case = env::var("IGNORE_CASE").map_or(false, |var| var.eq("1"));

  //   Ok(Config { query, file_path, ignore_case })
  // }

  // args: 改为 impl Iterator<Item = String> . 实现了迭代器特征的参数, 所以迭代器的所有权已经转移到 build
  // Result 可能包含一个 Config 实例，也可能包含一条错误信息 &str. 且拥有 'static 生命周期
  pub fn build(
    mut args: impl Iterator<Item = String>,
  ) -> Result<Config, &'static str> {
    // 第一个参数是程序名，由于无需使用，因此这里直接空调用一次
    args.next();

    // 第一个参数是 minigrep，由于无需使用，因此这里直接空调用一次
    args.next();

    let query = match args.next() {
      Some(arg) => arg,
      None => return Err("Didn't get a query string"),
    };

    let file_path = match args.next() {
      Some(arg) => arg,
      None => return Err("Didn't get a file path"),
    };

    // 检查环境配置里的 IGNORE_CASE 选项, 默认为 false. 传入 1 为 true.
    // 要想有, 命令行应该这么输入:
    // IGNORE_CASE=1 cargo run -- to poem.txt
    let ignore_case = env::var("IGNORE_CASE").map_or(false, |var| var.eq("1"));

    Ok(Config {
      query,
      file_path,
      ignore_case,
    })
  }
}

// Error 类型也可以是 io::Error
pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
  // todo 细化错误情况
  let file_path = &config.file_path;
  let contents = fs::read_to_string(file_path)?;

  let results = if config.ignore_case {
    search_case_insensitive(&config.query, &contents)
  } else {
    search(&config.query, &contents)
  };

  // ? 等效于
  // match contents {
  //   // 打开文件成功，将file句柄赋值给f
  //   Ok(file) => file,
  //   // 打开文件失败，将错误返回(向上传播)
  //   Err(e) => return Err(e),
  // };

  // println!("With text:\n{contents}");

  for line in results {
    println!("{line}");
  }

  Ok(())
}


pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
  // let mut results = Vec::new();
  // for line in contents.lines() {
  //   if line.contains(query) {
  //     results.push(line);
  //   }
  // }
  // results

  // 使用迭代器改进语法
  contents
    .lines()
    .filter(|line| line.contains(query))
    .collect()
}


pub fn search_case_insensitive<'a>(
  query: &str,
  contents: &'a str,
) -> Vec<&'a str> {
  let query = query.to_lowercase();
  let mut results = Vec::new();

  for line in contents.lines() {
    if line.to_lowercase().contains(&query) {
      results.push(line);
    }
  }

  results
}


// 添加库的测试代码
#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn one_result() {
    let query = "duct";
    let contents = "\
Rust:
safe, fast, productive.
Pick three.";
    assert_eq!(vec!["safe, fast, productive."], search(query, contents));
  }

  #[test]
  fn case_sensitive() {
    let query = "duct";
    let contents = "\
Rust:
safe, fast, productive.
Pick three.
Duct tape.";

    assert_eq!(vec!["safe, fast, productive."], search(query, contents));
  }

  #[test]
  fn case_insensitive() {
    let query = "rUsT";
    let contents = "\
Rust:
safe, fast, productive.
Pick three.
Trust me.";

    assert_eq!(
      vec!["Rust:", "Trust me."],
      search_case_insensitive(query, contents)
    );
  }
}
